package com.cehome.cloudbox.flowalbe;

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.validation.ProcessValidator;
import org.flowable.validation.ProcessValidatorFactory;
import org.flowable.validation.ValidationError;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Component
public class BpmnModelUtil {

    @Resource
    RepositoryService repositoryService;

    //获取图片
    public String getDeploymentModelImageByModelId(String deploymentId) throws IOException {
        if(deploymentId == null){
            return null;
        }

        List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
        if(names == null || names.size() == 0){
            return null;
        }

        String imageName = null;
        for(String name:names){
            if(name.indexOf(".png")>=0){
                imageName=name;
            }
        }

        InputStream in = repositoryService.getResourceAsStream(deploymentId,imageName);
        if(in == null){
            return null;
        }

        byte[] bytes = IOUtils.toByteArray(in);
        if(bytes == null){
            return null;
        }

        String encoded = Base64.encodeBase64String(bytes);
        if(encoded == null){
            return null;
        }

        encoded = "data:image/png;base64," + encoded;
        return encoded;
    }

    //发布流程
    public ProcessDefinition test(String activity,List<FlowElementPojo> objectList){
        Deployment deploy=null;
        try {

            BpmnModel bpmnModel=new BpmnModel();
            //设置流程信息
            //此信息都可以通过前期自定义数据,使用时再查询
            Process process = new Process();
            process.setId("test_model_3");
            process.setName("test_model_3");
            //添加流程节点信息---start
            String startId="startEvent_id_1";
            String startName="开始_1";
            String endId="endEvent_id_1";
            String endName="结束_1";
            //创建数组存储所有流程节点信息
            List<FlowElement> elementList=new ArrayList<>();
            //创建开始节点
            FlowElement startFlowElement=createStartFlowElement(startId,startName);
            FlowElement endFlowElement=createEndFlowElement(endId,endName);
            elementList.add(startFlowElement);
            elementList.add(endFlowElement);
            //查询各个节点的关系信息,并添加进流程
            List<FlowElementPojo> flowElementPojoList = new ArrayList<>();
            //查询普通任务节点信息
            elementList.addAll(findUserTaskElements(flowElementPojoList,startFlowElement,endFlowElement));
            //把节点放入process
            elementList.stream().forEach(item -> process.addFlowElement(item));
            for (FlowElementPojo flowElementPojo:flowElementPojoList){
                SequenceFlow sequenceFlow=createSequeneFlow(flowElementPojo.getId(),"流转",flowElementPojo.getResourceFlowElementId(),
                        flowElementPojo.getTargetFlowElementId(),"");
                process.addFlowElement(sequenceFlow);
            }

            bpmnModel.addProcess(process);

            //校验bpmModel
            ProcessValidator processValidator=new ProcessValidatorFactory().createDefaultProcessValidator();
            List<ValidationError> validationErrorList=processValidator.validate(bpmnModel);
            if (validationErrorList.size()>0){
                throw new RuntimeException("流程有误，请检查后重试");
            }

            String fileName="model_"+activity+"_bpmn20.xml";

            //生成自动布局
            BpmnXMLConverter bpmnXMLConverter=new BpmnXMLConverter();
            String bpmn=new String(bpmnXMLConverter.convertToXML(bpmnModel));
            System.out.println(bpmn);
            new BpmnAutoLayout(bpmnModel).execute();
            deploy =repositoryService.createDeployment().addBpmnModel(fileName,bpmnModel)
                    .tenantId("intelligentAsset")
                    .deploy();
            if(null!=deploy){
                ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
               return processDefinition;
            }
        }catch (Exception e){


        }finally {


        }
        return null;
    }

    /**
     * 创建开始节点信息
     * @return
     */
    public FlowElement createStartFlowElement(String id,String name){
        StartEvent startEvent=new StartEvent();
        startEvent.setId(id);
        startEvent.setName(name);
        return startEvent;
    }

    /**
     * 创建结束节点信息
     * @param id
     * @param name
     * @return
     */
    public FlowElement createEndFlowElement(String id,String name){
        EndEvent endEvent=new EndEvent();
        endEvent.setId(id);
        endEvent.setName(name);
        return endEvent;
    }
    /**
     * 查询各节点关联流转信息,即流转线
     *FlowElementPojo 是自定义类
     */
    public void createCirculationSequence(List<FlowElementPojo> objectList){
        //按顺序排好，
        Collections.sort(objectList);
        FlowElementPojo flowElementPojo_start=new FlowElementPojo();
        flowElementPojo_start.setId("sequence_id_1");
        flowElementPojo_start.setTargetFlowElementId("userTask_0");
        flowElementPojo_start.setResourceFlowElementId("startEvent_id_1");
        flowElementPojo_start.setFlowElementType("sequence");


        FlowElementPojo flowElementPojo_user_0=new FlowElementPojo();
        flowElementPojo_user_0.setId("sequence_id_2");
        flowElementPojo_user_0.setTargetFlowElementId("userTask_1");
        flowElementPojo_user_0.setResourceFlowElementId("userTask_0");
        flowElementPojo_user_0.setFlowElementType("sequence");

        FlowElementPojo flowElementPojo_user_1=new FlowElementPojo();
        flowElementPojo_user_1.setId("sequence_id_3");
        flowElementPojo_user_1.setTargetFlowElementId("userTask_2");
        flowElementPojo_user_1.setResourceFlowElementId("userTask_1");
        flowElementPojo_user_1.setFlowElementType("sequence");

        FlowElementPojo flowElementPojo_user_2=new FlowElementPojo();
        flowElementPojo_user_2.setId("sequence_id_4");
        flowElementPojo_user_2.setTargetFlowElementId("endEvent_id_1");
        flowElementPojo_user_2.setResourceFlowElementId("userTask_2");
        flowElementPojo_user_2.setFlowElementType("sequence");


    }

    //这里是生成实体任务节点
    public List<FlowElement> findUserTaskElements(List<FlowElementPojo> flowElementPojoList,FlowElement start,FlowElement end){
        List<FlowElement> flowElements = new ArrayList<>();
        //创建流程开始后的下一节点的审批参数节点，单节点
        UserTask userTask=new UserTask();
        userTask.setId("start_param_id");
        userTask.setName("选择审批对象");//这个节点是必须的
        userTask.setAssignee("${sendSaId}");
        String listParam_1 = "itemList1";//第一个多实例参数名称
        List<CustomProperty> customProperties = getCustomProperty(userTask.getId(),"true","true","role_admin","100",listParam_1);
        userTask.setCustomProperties(customProperties);
        flowElements.add(userTask);
        //创建流转线，将开始节点和选择审批节点链接起来
        FlowElementPojo flowElementPojo_start=new FlowElementPojo();
        flowElementPojo_start.setId("sequence_id_1");
        flowElementPojo_start.setTargetFlowElementId("start_param_id");
        flowElementPojo_start.setResourceFlowElementId(start.getId());
        flowElementPojo_start.setFlowElementType("sequence");
        flowElementPojoList.add(flowElementPojo_start);

        //创建三个任务节点，中间单实例，两头多实例
        //1、第一个多实例在开始的时候选择要审批的人或者权限，在点击发起的时候流程已发起，
        //创建多实例
        String nextParamName = "taskSaId";//下一节点任务人的名字，由多实例选择审批人，最后更新为准
        String taskId1 = "taskId1";
        FlowElement  flowElement= createMultiUserTask(taskId1,"行政多级审批1",listParam_1,nextParamName,"role_admin","false","true","1,2,3");
        flowElements.add(flowElement);
        //将审批节点和多实例链接起来
        FlowElementPojo flowElementPojo_user_0=new FlowElementPojo();
        flowElementPojo_user_0.setId("sequence_id_2");
        flowElementPojo_user_0.setTargetFlowElementId(taskId1);
        flowElementPojo_user_0.setResourceFlowElementId("start_param_id");
        flowElementPojo_user_0.setFlowElementType("sequence");
        flowElementPojoList.add(flowElementPojo_user_0);


        //创建单实例
        String taskId2 = "taskId2";
        UserTask userTask1=new UserTask();
        userTask1.setId(taskId2);
        userTask1.setName("单实例节点");
        userTask1.setAssignee("${taskSaId}");
        String nextParamNames = "itemList2";
        List<CustomProperty> customProperties1 = getCustomProperty(userTask1.getId(),"true","true","role_admin","1,2,3",nextParamNames);
        userTask1.setCustomProperties(customProperties1);
        flowElements.add(userTask1);
        //将多实例和单实例链接起来
        FlowElementPojo flowElementPojo_user_1=new FlowElementPojo();
        flowElementPojo_user_1.setId("sequence_id_3");
        flowElementPojo_user_1.setTargetFlowElementId("taskId2");
        flowElementPojo_user_1.setResourceFlowElementId("taskId1");
        flowElementPojo_user_1.setFlowElementType("sequence");
        flowElementPojoList.add(flowElementPojo_user_1);

        //创建多实例，不在选择审批人，此节点结束，任务结束
        String taskIdw = "taskIdw";
        FlowElement  last= createMultiUserTask(taskIdw,"行政多级审批2",nextParamNames,null,"role_admin","false","false","");
        flowElements.add(last);
        //将单实例和多实例链接起来
        FlowElementPojo flowElementPojo_user_2=new FlowElementPojo();
        flowElementPojo_user_2.setId("sequence_id_4");
        flowElementPojo_user_2.setTargetFlowElementId("taskIdw");
        flowElementPojo_user_2.setResourceFlowElementId("taskId2");
        flowElementPojo_user_2.setFlowElementType("sequence");
        flowElementPojoList.add(flowElementPojo_user_2);


        //最后将多实例节点和结束节点连接起来
        FlowElementPojo flowElementPojo_end=new FlowElementPojo();
        flowElementPojo_end.setId("sequence_id_5");
        flowElementPojo_end.setTargetFlowElementId(end.getId());
        flowElementPojo_end.setResourceFlowElementId("taskIdw");
        flowElementPojo_end.setFlowElementType("sequence");
        flowElementPojoList.add(flowElementPojo_end);
        return  flowElements;
    }

    public  List<CustomProperty> getCustomProperty(String userTaskId,String isHuiqian,String roleApprove,String roleStr,String persons,String nextParamName){
        List<CustomProperty> customProperties = new ArrayList<>();
        //会签参数
        CustomProperty customProperty = new CustomProperty();
        customProperty.setName(userTaskId+"huiq");
        customProperty.setSimpleValue(isHuiqian);
        customProperties.add(customProperty);
        //审批参数，是否是角色审批
        CustomProperty customProperty1 = new CustomProperty();
        customProperty1.setName(userTaskId+"roleApprove");
        customProperty1.setSimpleValue(roleApprove);
        customProperties.add(customProperty1);
        //角色字符串
        CustomProperty customProperty12 = new CustomProperty();
        customProperty12.setName(userTaskId+"roleStr");
        customProperty12.setSimpleValue(roleStr);
        customProperties.add(customProperty12);
        //指定审批人
        CustomProperty customProperty2 = new CustomProperty();
        customProperty2.setName(userTaskId+"persons");
        customProperty2.setSimpleValue(persons);
        customProperties.add(customProperty2);
        if(StringUtils.isNotBlank(nextParamName)){
            CustomProperty customProperty22 = new CustomProperty();
            customProperty22.setName(userTaskId+"nextParamName");
            customProperty22.setSimpleValue(nextParamName);
            customProperties.add(customProperty22);
        }
        return customProperties;
    }

    /**
     * 创建会签节点信息
     * @param id
     * @param name
     * @return
     */
    public FlowElement createMultiUserTask(String id,String name,String itemList,String nextParamName,String roleStr,String huiq,String roleApprove,String assignStrDot){
        UserTask userTask=new UserTask();
        userTask.setId(id);
        userTask.setName(name);
        //分配用户
        userTask.setAssignee("${assignee}");
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics=new MultiInstanceLoopCharacteristics();
//        multiInstanceLoopCharacteristics.setCollectionString("${collectionList}");
        //完成条件,默认所有人都完成
        multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfActiveInstances == nrOfInstances}");
        //元素变量多实例,一般和设置的assignee变量是对应的
        multiInstanceLoopCharacteristics.setElementVariable("assignee");
        //集合多实例,用于接收集合数据的表达式
        //multiInstanceLoopCharacteristics.setInputDataItem("${itemList}");
        multiInstanceLoopCharacteristics.setInputDataItem("${"+itemList+"}");
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        List<CustomProperty> customProperties = getCustomProperty(userTask.getId(),huiq,roleApprove,roleStr,assignStrDot,nextParamName);
        userTask.setCustomProperties(customProperties);
        return userTask;


    }




    /**
     * 绘制节点流转顺序
     * @param id
     * @param name
     * @param targetId
     * @param sourceId
     * @param conditionExpression
     * @return
     */
    public SequenceFlow createSequeneFlow(String id,String name,String sourceId,String targetId,String conditionExpression){
        SequenceFlow sequenceFlow=new SequenceFlow();
        sequenceFlow.setId(id);
        sequenceFlow.setName(name);
        if (ObjectUtils.isNotEmpty(targetId)){
            sequenceFlow.setTargetRef(targetId);
        }
        if (ObjectUtils.isNotEmpty(sourceId)){
            sequenceFlow.setSourceRef(sourceId);
        }
        if (ObjectUtils.isNotEmpty(conditionExpression)){
            sequenceFlow.setConditionExpression(conditionExpression);
        }
        return sequenceFlow;
    }


}
